#include <algorithm>
#include <iterator>
#include <iostream>
#include <map>
//#include <list>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <boost/format.hpp>
#include <boost/algorithm/string/case_conv.hpp>

#define POS_NOWHERE -1
#define POS_KILLED -2 //Also complete destroyed for objects
#define POS_CARRIED -3 //Object is being carried

using namespace std;
using boost::format;

int here = 0;
bool end = false;

map<string,int> verbs = {
  make_pair("e", 0),
  make_pair("east", 0),
  make_pair("s", 1),
  make_pair("south", 1),
  make_pair("w", 2),
  make_pair("west", 2),
  make_pair("n", 3),
  make_pair("north", 3),
  make_pair("l", 4),
  make_pair("look", 4),
  make_pair("take", 5),
  make_pair("get", 5),
  make_pair("drop", 6),
  make_pair("i", 7),
  make_pair("inv", 7),
  make_pair("inventory", 7),
  make_pair("examine", 8),
  make_pair("ex", 8),
  make_pair("go", 9),
  make_pair("play", 10),
  make_pair("use", 11)
  make_pair("?", -1)
};

struct Object {
  int pos;
  int weight; //<0: can not be carried
  string name;
  string desc;
};
vector<Object> objects = {
  { 0, 0, "lamp", "An old lamp, it uses oil for fuel, but the lamp is empty." },
  { 2, -1, "stream", "The stream flows very fast and is extremely cold" }, //1
  { 1, 0, "flute", "A very small platinium flute like those to call your dog" }, //2
  { POS_NOWHERE, -1, "cave", "The cave is hard to see, it is just a small fissure in the rocks" }, //3
  { 7, -1, "bats", "The bats sit in a quasi-random distribution on the mountain side." }, //4
  { 13, -1, "lake", "The lake is not very deep. It will reach up to your knees for most of the time."}, //5
  { POS_NOWHERE, 0, "radio", "This small transistor radio is smashed beyond any repair."}, //6
  { POS_NOWHERE, 0, "lamp", "The old lamp is half full with oil and will help you in dark caves." }, //7
  { 10, -1, "palm", "This palm tree is secreting an oily substance." }
};
//Object *objects_end = objects + sizeof(objects)/sizeof(Object);

struct Location {
  string desc_short;
  string desc_long;
  //struct Exit { int v; int to; } exits[];
  const char *exits;
  int go(int verb) const {
    int v, t;
    istringstream in(exits);

    if(verb == -1) return -1;
    while(in >> v >> t) {
      if(v == verb) return t;
    }
    return -1;
  }
};
vector<Location> locations = {
  { "A clearing in the forest.", "The forest around you seems dark and uninviting. It gets even thicker to the east. You can also see fog to the south.", "0 1 1 13 2 11 3 9" }, //0
  { "A very thick forest.", "There is absolutely no sound here.", "0 5 2 0 3 2" }, //1
  { "The end of the forst and beginning of a plain.", "The plain and the forest are divided by a fast flowing stream.", "0 3 1 1" }, //2
  { "A plain.", "The plain is very monotonous.", "0 4 1 5 2 2"}, //3
  { "A plain.", "Very far away there are some mountains.", "0 6 2 3"}, //4
  { "A plain.", "The plain is very monotonous. To the west there is a forest.", "2 1 3 3"}, //5
  { "A plain.", "The mountains appear much close now.", "0 7 2 4"}, //6
  { "The mountains.", "The mountains are very steep and rocky. There appears to be no possibility to climb them.", "2 6"}, //7
  { "A dark cave.", "The light from your lamp is barely sufficient to light this cave.", "2 7" }, //8
  { "A forest.", "This is a normal forest.", "1 0 2 10"}, //9
  { "A calm forest.", "This is a calming and relaxing forrest. You feal mother nature watching out for you.", "0 9 1 11"}, //10
  { "A forest.", "This is a normal forrest.", "0 0 1 12 3 10"}, //11
  { "A forest bordering to a mountain.", "The forrest seems to end here as a cliff presents you with a vertical surface.", "0 13 3 11"}, //12
  { "A forest close to a lake.", "The forrest thins a little here. Mist swirls on the lake surface.", "2 12 3 0"} //13
};
/*
 *
 *     	       	 10----9    2----3----4---6---7 -<?- 8
 *     	       	  |    |    | 	 |
 *		  |    |    |	 |
 * 		 11----0----1----5
 *		  |    |
 *		  |    |
 *		 12---13
 *
 */

vector<string> msgs = {
  "If you explain to me how to pick up a stream...",
  "The water immediately sucks you in and you drown.",
  "You play the flute but you hear no sound.", //2
  "Bats are roused by your playing and you discover a cave.", //3
  "The cave is too dark to enter. Maybe there are more bats or even vampires!", //4
  "While walking in the lake you found an old and broken transistor radio.", //5
  "A nice and refreshing walk through the lake.", //6
  "Well, it is too heavy to carry and you can not uproot it anyway.", //7
  "You can not carry the palm but you manage to scrape some of the oil into the lamp."
};

struct Cmd {
  int c;
  int p1, p2;

  Cmd(int i = -1, int p1_ = -1, int p2_ = -1) : c(i), p1(p1_), p2(p2_) {}
};
struct CCmd {
  int c;
  int p1, p2;

  CCmd(int i = -1, int p1_ = -1, int p2_ = -1) : c(i), p1(p1_), p2(p2_) {}
};
struct ACmd {
  int c;
  int p1, p2;

  ACmd(int i = -1, int p1_ = -1, int p2_ = -1) : c(i), p1(p1_), p2(p2_) {}
};
struct Event {
  int verb;
  int object;
  vector<CCmd> conditions;
  vector<ACmd> actions;
  //this even does work...: vector<ACmd*> test;
};
Event events[] = {
  { 4, -1, {}, {ACmd('l')} },
  { 5, 1, {CCmd('p')}, {ACmd('P', 0)} },
  { 9, 1, {CCmd('p')}, {ACmd('P', 1), ACmd(0xDEAD)} },
  { 9, 3, {CCmd('l', 7), CCmd('C', 7)}, {ACmd('g', 8)}},
  { 9, 3, {CCmd('l', 7), }, {ACmd('P', 4)}},
  { 9/*go*/, 5/*lake*/, {CCmd('l', 13), CCmd('Y', 6/*radio*/)}, {ACmd('P', 5), ACmd('M', 6, -2)}},
  { 9/*go*/, 5/*lake*/, {CCmd('l', 13)}, {ACmd('P', 6)}},
  { 11, 0, {CCmd('p'), CCmd('P',8)}, {ACmd('P',8), ACmd('Y',0), ACmd('M',7,POS_CARRIED)} }, /*If "use lamp" and lamp and palm nearby then print message, destroy lamp and replace with lamp (half full of oil). Puh!*/
  { 5, 8, {CCmd('p'), CCmd('P',0)}, {ACmd('P',8), ACmd('Y',0), ACmd('M',7,POS_CARRIED)} }, /*If "take palm" and lamp and palm nearby then print message, destroy lamp and replace with lamp (half full of oil). Puh!*/
  { 5, 8, {CCmd('p')}, {ACmd('P',7)} },
  { 5, -2, {CCmd('p')}, {ACmd('p')} },
  { 6, -2, {CCmd('c')}, {ACmd('d')} },
  { 7, -1, {}, {ACmd('i')} },
  { 8, -2, {CCmd('p')}, {ACmd('e')} },
  { 10, 2, {CCmd('c'), CCmd('l', 7), CCmd('P', 4)}, {ACmd('P', 3), ACmd('M', 3, 7), ACmd('Y', 4)} },
  { 10, 2, {CCmd('c')}, {ACmd('P', 2)}, /*{new ACmd(0,0,0)}*/ }
};
const Event* events_end = events + sizeof(events)/sizeof(Event);

int find_object(const string &obj) {
  vector<Object>::const_iterator it;
  for(int i = 0; i < objects.size(); ++i) {
    Object &curr(objects[i]);
    if(curr.name == obj && curr.pos != POS_KILLED) return i;
  }
  return -1;
}

int do_action(int v, int o) {
  for(Event* p = events; p < events_end; ++p) {
    if(p->verb == v && (p->object == o || (p->object == -2 && o >= 0))) {
      for(int i = 0; i < p->conditions.size(); ++i) {
	int j;
	CCmd ccmd = p->conditions[i];
	switch(ccmd.c) {
	case 'l': //Check location
	  if(ccmd.p1 != here)
	    goto cond_fail;
	  break;
	case 'p': //Object present
	  if(objects[o].pos != here && objects[o].pos != POS_CARRIED)
	    goto cond_fail;
	  break;
	case 'P': //Object ? present
	  if(objects[ccmd.p1].pos != here && objects[ccmd.p1].pos != POS_CARRIED)
	    goto cond_fail;
	  break;
	case 'c': //Carrying object
	  if(objects[o].pos != POS_CARRIED)
	    goto cond_fail;
	  break;
	case 'C': //Carrying object ?
	  if(objects[ccmd.p1].pos != POS_CARRIED)
	    goto cond_fail;
	  break;
	case 'Y': //Object ? destroyed?
	  if(objects[ccmd.p1].pos != POS_KILLED)
	    goto cond_fail;
	  break;
	default:
	  throw runtime_error(str(format("unknown condition %d $%X '%c'") % ccmd.c % ccmd.c % static_cast<char>(ccmd.c)));
	}
      }
      for(int i = 0; i < p->actions.size(); ++i) {
	ACmd acmd = p->actions[i];
	switch(acmd.c) {
	case 0xDEAD: //dead
	  here = -1;
	  break;
	case -1:
	  goto cond_fail;
	case 'd': //drop
	  objects[o].pos = here;
	  break;
	case 'e': //examine
	  cout << objects[o].desc << ".\n";
	  break;
	case 'g': //go to
	  here = acmd.p1;
	  break;
	case 'i': //Inv
	  cout << "You carry: ";
	  for(int j = 0; j < objects.size(); ++j) {
	    if(objects[j].pos == POS_CARRIED) cout << objects[j].name << ' ';
	  }
	  cout << '\n';
	  break;
	case 'l':
	  cout << locations[here].desc_long << ' ';
	  break;
	case 'p': //pick up
	  if(objects[o].weight < 0)
	    cout << "You can not carry that!\n";
	  else
	    objects[o].pos = POS_CARRIED;
	  break;
	case 'y': //destroy current object
	  objects[o].pos = POS_KILLED;
	  break;
	case 'Y': //destroy object ?
	  objects[acmd.p1].pos = POS_KILLED;
	  break;
	case 'P': //print msg
	  cout << msgs[acmd.p1] << endl;
	  break;
	case 'M': //move object to
	  objects[acmd.p1].pos = acmd.p2;
	  break;
	default:
	  throw runtime_error(str(format("unknown action %d $%X '%c'") % acmd.c % acmd.c % static_cast<char>(acmd.c)));
	}
      }
      return 1;
    cond_fail:
      ;
    }
  }
  return 0; //Nothing done
}

void print_objects() {
  bool none = true;

  for(unsigned int i = 0; i < objects.size(); ++i) {
    if(objects[i].pos == here) {
      if(none) {
	none = false;
	cout << "You can see: ";
      }
      cout << objects[i].name << ' ';
    }
  }
  if(!none) cout << ". ";
  cout << ' ';
}

int main(int argc, char *argv[]) {
  string line;

  do {
    string verb, object;
    int verbn = -1;
    int objectn = -1;

    cout << locations[here].desc_short << ' ';
    print_objects();
    cout << "Exits are: ";
    if(locations[here].go(verbs["e"]) >= 0) cout << "E ";
    if(locations[here].go(verbs["s"]) >= 0) cout << "S ";
    if(locations[here].go(verbs["w"]) >= 0) cout << "W ";
    if(locations[here].go(verbs["n"]) >= 0) cout << "N ";
    cout << endl << "? ";
    getline(cin, line);
    boost::to_lower(line);
    istringstream in(line);
    if(in >> verb) if(!(in >> object)) objectn = -1;
    //cout << verb + ' ' + object << endl;
    verbn = verbs.find(verb) != verbs.end() ? verbs.find(verb)->second : -1;
    objectn = find_object(object);
    //cout << verbn << '\t' << objectn << endl;
    if(verbn < 0) {
      cout << "I do not know how to `" << verb << "`.\n";
    } else if(objectn == -2) {
      cout << "I do not know what a `" << object << "` is.\n";
    } else {
      if(objectn < -1) objectn = -1;
      if(!do_action(verbn, objectn)) {
	int to;
	if((to = locations[here].go(verbn)) >= 0) {
	  here = to;
	} else {
	  if(verbn < 4)
	    cout << "You can not go in that direction.\n";
	  else
	    cout << "I do not understand!\n";
	}
      }
    }
    if(here == -1) {
      cout << "You are dead!\n";
      end = true;
    }
  } while(!end);
  return 0;
}
